<?php

namespace app\admin\model\order;

use app\event\mobile\MobileSupplierFail;
use think\Exception;
use think\facade\Db;
use think\facade\Log;
use app\common\model\Export;
use app\common\model\Mobile as MobileModel;

class Mobile
{
    private $table = 'mobile_order';
    private $error = '';

    /**
     * 列表.
     */
    public function getList($params)
    {
        $where = [];
        $where[] = ['status', '=', 2];
        $agentName = $params['agent_name'] ?? '';
        if ($agentName) {
            $agentId = Db::name('agent')
                ->where('username|company_name', 'like', '%' . $agentName . '%')
                ->column('id');
            if ($agentId) {
                $where[] = ['agent_id', 'in', $agentId];
            } else {
                $where[] = ['agent_id', '=', -1];
            }
        }
        // 业务员搜索
        $salesmanTrueName = $params['salesman_true_name'] ?? '';
        if ($salesmanTrueName) {
            $salesmanId = Db::name('salesman')
                ->where('true_name', 'like', '%' . $salesmanTrueName . '%')
                ->column('id');
            if ($salesmanId) {
                $where[] = ['salesman_id', 'in', $salesmanId];
            } else {
                $where[] = ['salesman_id', '=', -1];
            }
        }

        $mobile = trim($params['mobile'] ?? '');
        if ($mobile) {
            $where[] = ['mobile', 'like', '%' . $mobile . '%'];
        }
        $keyword = trim($params['keyword'] ?? '');

        $keyword = str_replace(["'",'"','“','’'],'',$keyword);

        if(strstr($keyword, "\n")) {
            $keyword = explode("\n",$keyword);
        }

        if (is_array($keyword)) { // 多手机号搜索
            $where[] = ['mobile', 'in', $keyword];
        }

        if (is_string($keyword)) {
            $where[] = ['mobile|sub_order_id|out_order_id', 'like', '%' . $keyword . '%'];
        }
        if (!empty($params['mobile_supplier_id'])) {
            $where[] = ['mobile_supplier_id', '=', $params['mobile_supplier_id']];
        }
        if (!empty($params['order_type'])) {
            $where[] = ['order_type', '=', $params['order_type']];
        }
        if (!empty($params['supplier_status'])) {
            if (in_array($params['supplier_status'], [3, 4])) {
                $where[] = ['supplier_status', '=', $params['supplier_status']];
            } else {
                $where[] = ['supplier_status', 'in', [1, 2, 21, 99]];
            }
        }
        if (!empty($params['refund_status'])) {
            if (2 == $params['refund_status']) {
                $where[] = ['supplier_status', '=', 4];
            }
            $where[] = ['refund_status', '=', $params['refund_status']];
        }
        $createdDate = $params['createdDate'] ?? [];
        if (is_array($createdDate) && !empty($createdDate[0]) && $createdDate[1]) {
            $where[] = ['created_at', 'between time', [$createdDate[0], $createdDate[1] . ':59']];
        }
        // 供应商
        $supplierList = Db::name('mobile_supplier')
            ->field('id, name')
            ->where('is_deleted', 0)
            ->select()
            ->toArray();
        $supplier = array_column($supplierList, 'name', 'id');

        $paginate = [
            'page' => $params['page'] ?? 1,
            'list_rows' => $params['list_rows'] ?? 10,
        ];
        $db = Db::name($this->table)
            ->where($where)
            ->order('id desc');
        if (!empty($params['is_export'])) {
            //返回导出excel 需要的数据
            $list = $db->order('id desc')->select()->toArray();
            $agentIds = array_column($list, 'agent_id');
            // 代理商
            $agent = Db::name('agent')
                ->whereIn('id', $agentIds)
                ->order('username asc')
                ->column('username, company_name', 'id');
            // 业务员
            $salesmanIds = array_column($list, 'salesman_id');
            $salesmanArr = Db::name('salesman')->whereIn('id', $salesmanIds)->order('id desc')->column('true_name', 'id');
            $res = [];
            $title = [
                '供应商订单号', '系统订单号', 'API接口订单号', '供应商', '代理商用户名', '代理商公司名称', '所属业务员',
                '销售价', '结算价', '成本价', '代理商利润', '平台利润', '充值号码', '充值金额', '运营商', '充值类型',
                '充值状态', '下单时间', '完成时间', '备注', '时长'
            ];
            $statusMap = [
                3 => '充值成功',
                4 => '充值失败',
            ];
            $ispTypeMap = [
                1 => '移动',
                2 => '联通',
                3 => '电信',
                -1 => '未知',
            ];

            foreach ($list as $v) {
                $ispType = getMobileIsp($v['mobile']);
                $spend = '';
                if ($v['finish_time']) {
                    $spend = getRemainderTime(strtotime($v['created_at']), $v['finish_time']);
                    $v['finish_time'] = date('Y-m-d H:i:s', $v['finish_time']);
                } else {
                    $v['finish_time'] = '';
                }

                $res[] = [
                    $v['supplier_order_no'] . "\t",
                    $v['sub_order_id'] . "\t",
                    $v['out_order_id'] . "\t",
                    $supplier[$v['mobile_supplier_id']] ?? '',
                    $agent[$v['agent_id']]['username'],
                    $agent[$v['agent_id']]['company_name'],
                    $salesmanArr[$v['salesman_id']] ?? '',
                    fen2yuan($v['sale_price']) . '',
                    fen2yuan($v['settle_price']) . '',
                    fen2yuan($v['supplier_price']) . '',
                    fen2yuan($v['profit']) . '',
                    fen2yuan($v['platform_profit']) . '',
                    $v['mobile'],
                    fen2yuan($v['amount']),
                    $ispTypeMap[$ispType],
                    $v['order_type'] == 1 ? '慢充' : '快充',
                    $statusMap[$v['supplier_status']] ?? '充值中',
                    $v['created_at'],
                    $v['finish_time'],
                    $v['remark'],
                    $spend,
                ];
            }
            unset($list);
            $export = new Export;
            $name = '话费订单';
            return $export->downloadExcel($title, $res, $name);
        } else {
            $list = $db->order('id desc')->paginate($paginate)->toArray();
        }
        if ($list['data']) {
            $agentIds = array_column($list['data'], 'agent_id');
            // 代理商
            $agent = Db::name('agent')
                ->whereIn('id', $agentIds)
                ->order('username asc')
                ->column('username, company_name', 'id');
            // 业务员
            $salesmanIds = array_column($list['data'], 'salesman_id');
            $salesmanArr = Db::name('salesman')->whereIn('id', $salesmanIds)->order('id desc')->column('true_name', 'id');
            foreach ($list['data'] as $k => $v) {
                $list['data'][$k]['agent_name'] = $agent[$v['agent_id']]['username'] ?? '';
                $list['data'][$k]['company_name'] = $agent[$v['agent_id']]['company_name'] ?? '';
                $list['data'][$k]['supplier_name'] = $supplier[$v['mobile_supplier_id']] ?? '券妈妈';
                $list['data'][$k]['salesman_true_name'] = $salesmanArr[$v['salesman_id']] ?? '';
                $list['data'][$k]['isp_type'] = getMobileIsp($v['mobile']);
                // 测试环境的订单编号22032919115485615 返回josn格式时 被改成22032919115485616 暂时不太清楚为啥json为
                $list['data'][$k]['order_id'] = strval($v['order_id']);

                if ($v['finish_time']) {
                    $list['data'][$k]['finish_time'] = date('Y-m-d H:i:s', $v['finish_time']);
                    $list['data'][$k]['spend'] = getRemainderTime(strtotime($v['created_at']),$v['finish_time']);
                }
                // 异常订单，状态改成充值中，前端就可以后台批量失败
                if ($v['supplier_status'] == 21) {
                    $list['data'][$k]['supplier_status'] = 2;
                }
            }
        }
        $list['supplierList'] = $supplierList;

        return success('操作成功', $list);
    }



    /**
     * 退款.
     */
    public function refund($id)
    {
        $lock_key = 'mobile_refund_lock_' . $id;
        $val = cache($lock_key);
        if ($val) {
            $this->error = '退款中';

            return false;
        }
        cache($lock_key, 1, 1200);

        $order = Db::name($this->table)
            ->where('id', $id)
            ->find();
        if (empty($order)) {
            $this->error = '订单不存在';

            return false;
        }

        if (2 != $order['status']) {
            $this->error = '该订单尚未支付';

            return false;
        }
        if ($order['supplier_status'] != 4) {
            $this->error = '充值失败的订单才能退款【' . $order['supplier_status'] . '】';

            return false;
        }
        if ($order['refund_status'] != 2) {
            $this->error = '退款状态有误【' . $order['refund_status'] . '】';

            return false;
        }

        $res = Db::name($this->table)
            ->where('id', $id)
            ->where('refund_status', 2)
            ->update([
                'refund_status' => 4, // 退款中
            ]);
        if (!$res) {
            $this->error = '退款状态有误' . __LINE__;
            return false;
        }

        $refundFee = 0;
        $refundSettleFee = $order['settle_price'];
        if (
            $order['order_from'] == ORDER_FROM_H5
            || ($order['order_from'] == ORDER_FROM_API && $order['settle_type'] == SETTLE_TYPE_PROFIT)
        ) {
            // H5 过来的订单或者api返佣的，才有在线支付
            $payLog = Db::name('pay_log')
                ->where('order_id', $order['order_id'])
                ->where('status', 2)
                ->find();
            if (empty($payLog)) {
                $this->error = '支付记录不存在';

                return false;
            }

            $refundFee = $payLog['real_pay_price'];
            $refundSettleFee = $payLog['settle_price'];

            $payLog['refund_fee'] = $refundFee;
            if (1 == $payLog['pay_channel']) {
                $res = $this->wxpayRefund($payLog);
            } else {
                $res = $this->alipayRefund($payLog);
            }
            if (false === $res) {
                return false;
            }
        }

        Db::startTrans();
        try {
            $res = Db::name($this->table)
                ->where('id', $order['id'])
                ->where('refund_status', 4)
                ->update([
                    'refund_status' => 3,
                ]);
            if (!$res) {
                Db::rollback();

                $this->error = '退款失败：重复退款';
                return false;
            }

            // agent_balance_log 这张表有百万级别的数据，之前这边没加 type quote_id 索引，导致查询非常慢，100 条退款要执行一分多种
            // 加了索引之后，100 条退款只要 6 秒左右
            $exists = Db::name('agent_balance_log')
                ->where('type', BALANCE_MOBILE_REFUND)
                ->where('quote_id', $order['id'])
                ->value('id');
            if ($exists) {
                Db::rollback();

                $this->error = '重复退款';
                return false;
            }

            // 预付的，要给代理商退钱
            if (SETTLE_TYPE_PREPAY == $order['settle_type']) {
                $balance = Db::name('agent')
                    ->where('id', $order['agent_id'])
                    ->lock(true)
                    ->value('balance');
                $newBalance = $balance + $refundSettleFee;
                Db::name('agent')
                    ->where('id', $order['agent_id'])
                    ->inc('balance', $refundSettleFee)
                    ->update([
                        'balance_last_updated_at' => date('Y-m-d H:i:s'),
                    ]);
                Db::name('agent_balance_log')
                    ->insert([
                        'agent_id' => $order['agent_id'],
                        'type' => BALANCE_MOBILE_REFUND,
                        'amount' => $refundSettleFee,
                        'old_balance' => $balance,
                        'new_balance' => $newBalance,
                        'remark' => '话费充值失败退款：' . fen2yuan($refundSettleFee) . ' 元。订单号：' . $order['sub_order_id'],
                        'quote_id' => $order['id'],
                        'trade_no' => $order['sub_order_id'],
                    ]);
            }
            // 返佣已结算的，要扣回代理商佣金
            if (SETTLE_TYPE_PROFIT == $order['settle_type'] && $order['settle_status'] == 3 && $order['profit'] > 0) {
                $profitBalance = Db::name('agent')
                    ->where('id', $order['agent_id'])
                    ->value('profit_balance');
                Db::name('agent')
                    ->where('id', $order['agent_id'])
                    ->dec('profit_balance', $order['profit'])
                    ->dec('total_profit', $order['profit'])
                    ->update([
                        'balance_last_updated_at' => date('Y-m-d H:i:s'),
                    ]);
                Db::name('agent_balance_log')
                    ->insert([
                        'agent_id' => $order['agent_id'],
                        'type' => BALANCE_MOBILE_PROFIT,
                        'amount' => -$order['profit'],
                        'old_balance' => $profitBalance,
                        'new_balance' => $profitBalance - $order['profit'],
                        'remark' => '话费充值失败退回佣金：' . fen2yuan($order['amount']) . '元，销售价：' . fen2yuan($order['sale_price']) . '元，结算价：' . fen2yuan($order['settle_price']) . '元，利润：' . fen2yuan($order['profit']) . '元。订单号：' . $order['sub_order_id'],
                        'quote_id' => $order['id'],
                        'trade_no' => $order['sub_order_id'],
                    ]);
            }

            if (
                $order['order_from'] == ORDER_FROM_H5
                || ($order['order_from'] == ORDER_FROM_API && $order['settle_type'] == SETTLE_TYPE_PROFIT)
            ) {
                Db::name('pay_log')
                    ->where('id', $payLog['id'])
                    ->inc('refund_amount', $refundFee)
                    ->inc('refund_settle_amount', $refundSettleFee)
                    ->update();
            }

            try {
                //话费充值失败了
                event(new MobileSupplierFail($order['order_id']));
            }catch (Exception $exception){
                Log::error('话费充值失败');
            }


            Db::commit();
        } catch (\Exception $e) {
            Db::rollback();

            $this->error = '退款失败：' . $e->getMessage();

            return false;
        }

        return true;
    }

    /**
     * 微信退款.
     */
    private function wxpayRefund($payLog)
    {
        // 测试环境，2022-03-30 之前的订单，直接返回退款成功，不做退款处理
        if ($payLog['created_at'] < '2023-03-25') {
            return true;
        }

        try {
            if (SETTLE_TYPE_PREPAY == $payLog['settle_type']) {
                $wxpay = new \app\common\model\Wxpay($payLog['agent_id']);
            } else {
                $type = 'wechat';
                if ('MWEB' == $payLog['trade_type']) {
                    $type = 'wechat_mweb';
                }
                $wxpay = new \app\common\model\Wxpay(0, $type);
            }
            $data = [
                'transactionId' => $payLog['trade_no'],
                'refundNumber' => $payLog['order_id'],
                'totalFee' => $payLog['real_pay_price'],
                'refundFee' => $payLog['refund_fee'],
                'desc' => '充值失败退款',
            ];
            $res = $wxpay->refund($data);
        } catch (\Exception $e) {
            $this->error = $e->getMessage();

            return false;
        }

        if (isset($res['err_code_des']) && $res['err_code_des'] == '订单已全额退款') {
            return true;
        }

        if (!isset($res['result_code']) || $res['result_code'] != 'SUCCESS') {
            $this->error = '退款失败：' . json_encode($res, JSON_UNESCAPED_UNICODE);

            return false;
        }

        return true;
    }

    /**
     * 支付宝退款.
     */
    private function alipayRefund($payLog)
    {
        try {
            if (SETTLE_TYPE_PREPAY == $payLog['settle_type']) {
                $alipay = new \app\common\model\Alipay($payLog['agent_id']);
            } else {
                $alipay = new \app\common\model\Alipay();
            }
            $data = [
                'out_trade_no' => $payLog['order_id'],
                'refund_amount' => fen2yuan($payLog['real_pay_price']),
                'refund_reason' => '充值失败退款',
            ];
            $res = $alipay->refund($data)->toArray();
            Log::info(json_encode($res, JSON_UNESCAPED_UNICODE));
        } catch (\Exception $e) {
            $this->error = $e->getMessage();

            return false;
        }

        if (!isset($res['code']) || $res['code'] != '10000' || empty($res['refund_fee'])) {
            $this->error = '退款失败：' . json_encode($res, JSON_UNESCAPED_UNICODE);

            return false;
        }

        return true;
    }

    /**
     * 手动修改充值状态
     */
    public function update($params)
    {
        if (empty($params['ids'])) {
            return fail('请先选中需要更改的充值记录');
        }
        if (empty($params['status']) || !in_array($params['status'], [1, 2])) {
            return fail('状态有误');
        }

        $now = time();

        // 充值失败
        $updateFail = [
            'remark' => '后台手动充值失败',
            'refund_status' => 2,
            'supplier_status' => 4,
            'finish_time' => $now,
        ];
        $updateFailIds = [];

        // 充值成功，预充模式
        $updateSuccess = [
            'remark' => '后台手动充值成功',
            'supplier_status' => 3,
            'finish_time' => $now,
        ];
        $updateSuccessIds = [];

        // 充值成功，返佣模式
        $updateSuccess1 = [
            'remark' => '后台手动充值成功',
            'supplier_status' => 3,
            'settle_status' => 3,
            'finish_time' => $now,
        ];
        $updateSuccessIds1 = [];
        $updateSuccess2 = [
            'remark' => '后台手动充值成功',
            'supplier_status' => 3,
            'settle_status' => 2,
            'finish_time' => $now,
        ];
        $updateSuccessIds2 = [];
        if (2 == $params['status']) {
            $list = Db::name($this->table)
                // ->field('id, settle_type, profit, settle_status')
                ->whereIn('id', $params['ids'])
                ->where('status', 2)
                ->whereIn('supplier_status', [1, 2, 21])
                ->select()
                ->toArray();
            $updateFailIds = array_column($list, 'id');
        } else {
            $list = Db::name($this->table)
                // ->field('id, settle_type, profit, settle_status')
                ->whereIn('id', $params['ids'])
                ->where('status', 2)
                ->whereIn('supplier_status', [1, 2, 21])
                ->select()
                ->toArray();
            foreach ($list as $k => $v) {
                if (SETTLE_TYPE_PROFIT == $v['settle_type']) {
                    if (0 == $v['profit']) {
                        $updateSuccessIds1[] = $v['id'];
                    } elseif (1 == $v['settle_status']) {
                        $updateSuccessIds2[] = $v['id'];
                    }
                } else {
                    $updateSuccessIds[] = $v['id'];
                }
            }
        }

        Db::startTrans();
        try {
            foreach ($list as $v) {
                MobileModel::handleNotify($v, [
                    'supplier_status' => (2 == $params['status']) ? 4 : 3,
                ]);
            }

            if ($updateFailIds) {
                Db::name($this->table)
                    ->whereIn('id', $updateFailIds)
                    ->update($updateFail);
            }
            if ($updateSuccessIds) {
                Db::name($this->table)
                    ->whereIn('id', $updateSuccessIds)
                    ->update($updateSuccess);
            }
            if ($updateSuccessIds1) {
                Db::name($this->table)
                    ->whereIn('id', $updateSuccessIds1)
                    ->update($updateSuccess1);
            }
            if ($updateSuccessIds2) {
                Db::name($this->table)
                    ->whereIn('id', $updateSuccessIds2)
                    ->update($updateSuccess2);
            }

            Db::commit();
        } catch (\Exception $e) {
            Db::rollback();

            return fail('修改失败：' . $e->getMessage());
        }

        return success('修改成功');
    }

    public function getError()
    {
        return $this->error;
    }

    /**
     * API 订单手动回调.
     */
    public function manualNotify($params)
    {
        if (empty($params['id']) && empty($params['order_id'])) {
            $this->error = '参数有误';

            return false;
        }

        $order = Db::name('mobile_order')
            ->where($params)
            ->find();
        if ($order['order_from'] != ORDER_FROM_API) {
            $this->error = '该订单不是 API 接口订单，无法回调';

            return false;
        }
        if (empty($order['notify_url'])) {
            $this->error = '该订单没有设置回调地址，无法回调';

            return false;
        }
        $agent = Db::name('agent')
            ->field('app_key, app_secret')
            ->where('id', $order['agent_id'])
            ->find();

        $status = $order['supplier_status'];
        if ($status == 1 && $order['status'] == 2) {
            $status = 2;
        }

        $notifyData = [
            'timestamp' => time(),
            'order_id' => strval($order['order_id']),
            'out_order_id' => $order['out_order_id'],
            'status' => $status,
            'mobile' => $order['mobile'],
            'amount' => fen2yuan($order['amount']),
            'app_key' => $agent['app_key'],
            'official_sn' => $order['official_sn'] ?? '',
        ];
        $notifyData['sign'] = getSign($notifyData, $agent['app_secret']);
        $notifyData = json_encode($notifyData);
        Log::write('api 返佣订单通知：' . $notifyData, 'notice');
        $res = curlPost($order['notify_url'], $notifyData, [
            'Content-type: application/json;charset=utf-8',
        ]);
        if ($res === 'SUCCESS' || $res === 'ok') {
            return true;
        }

        $this->error = '通知失败：' . $res;

        return false;
    }

    /**
     * 下单失败不再切换通道.
     */
    public function failNotChange($params)
    {
        if (empty($params['id'])) {
            $this->error = '参数有误';

            return false;
        }

        Db::name('mobile_order')
            ->where('id', $params['id'])
            ->update([
                'change_flag' => 0,
            ]);

        return true;
    }

    /**
     * 订单轮询记录
     */
    public function history($originId)
    {
        $list = Db::name('mobile_order_history')
            ->where('origin_id', $originId)
            ->order('id asc')
            ->select()
            ->toArray();
        $current = Db::name('mobile_order')
            ->where('id', $originId)
            ->find();
        $list[] = $current;
        $supplierIds = array_unique(array_column($list, 'mobile_supplier_id'));
        $supplier = Db::name('mobile_supplier')
            ->whereIn('id', $supplierIds)
            ->column('name', 'id');
        foreach ($list as $k => $v) {
            if ($v['supplier_order_create_time']) {
                $list[$k]['created_at'] = date('Y-m-d H:i:s', $v['supplier_order_create_time']);
            }
            $list[$k]['supplier_name'] = $supplier[$v['mobile_supplier_id']] ?? '';
            if ($v['finish_time']) {
                $list[$k]['finish_time'] = date('Y-m-d H:i:s', $v['finish_time']);
            } else {
                $list[$k]['finish_time'] = '';
            }
            if ($v['supplier_order_create_time'] && $v['finish_time']) {
                $list[$k]['spend'] = getRemainderTime($v['supplier_order_create_time'], $v['finish_time']);
            }
            $list[$k]['isp_type'] = getMobileIsp($v['mobile']);
        }

        return $list;
    }
}
